package co.fitstart.market.service.impl;

import java.util.Arrays;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.demo2do.core.persistence.GenericDaoSupport;
import com.demo2do.core.persistence.annotation.Dynamic;
import com.demo2do.core.utils.DateFormatUtils;

import co.fitstart.entity.market.AchievementStatus;
import co.fitstart.entity.market.AchievementType;
import co.fitstart.entity.market.CoursewareStatus;
import co.fitstart.entity.market.CreditRecord;
import co.fitstart.entity.market.DailyStatus;
import co.fitstart.entity.market.MarketAchievement;
import co.fitstart.entity.market.MarketBatchCourseware;
import co.fitstart.entity.market.MarketCourse;
import co.fitstart.entity.market.MarketCourseRound;
import co.fitstart.entity.market.MarketDailyCourseware;
import co.fitstart.entity.market.MarketMonthlyCourseware;
import co.fitstart.entity.market.MarketRecommendRecord;
import co.fitstart.entity.market.MarketWechatImage;
import co.fitstart.entity.market.TrainingRecord;
import co.fitstart.entity.market.TrainingStatus;
import co.fitstart.entity.user.User;
import co.fitstart.market.dao.CoursewareDao;
import co.fitstart.market.dao.UserDao;
import co.fitstart.market.handler.SmsSender;
import co.fitstart.market.service.CoursewareService;

/**
 * 
 * @author dan
 *
 */
@Service("coursewareService")
public class CoursewareServiceImpl implements CoursewareService{
    
    @Dynamic
    private CoursewareDao coursewareDao;
    
    @Dynamic
    private UserDao userDao;
    
    @Autowired
    private GenericDaoSupport genericDaoSupport;
    
    @Autowired
    private SmsSender smsSender;
    
    @Value("#{ctx['system.host']}")
    private String path;
    
    private static final List<Integer> amounts = Arrays.asList(3, 7, 30, 60, 90, 120);
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.market.service.CoursewareService#loadCourse(java.lang.Long)
     */
    public MarketCourse loadCourse(Long courseId) {
        return genericDaoSupport.load(MarketCourse.class, courseId);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.market.service.CoursewareService#listCourse()
     */
    public List<MarketCourse> listCourse() {
        return coursewareDao.listMarketCourse();
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.market.service.CoursewareService#listMonthlyCourseware(co.fitstart.entity.market.MarketCourseRound)
     */
    public List<MarketMonthlyCourseware> listMonthlyCourseware(MarketCourseRound courseRound) {
        return coursewareDao.listMonthlyCourseware(courseRound);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.market.service.CoursewareService#loadMonthlyCourseware(co.fitstart.entity.market.MarketCourseRound)
     */
    public MarketMonthlyCourseware loadMonthlyCourseware(MarketCourseRound courseRound) {
        return coursewareDao.loadMonthlyCourseware(courseRound, CoursewareStatus.FINISHED);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.market.service.CoursewareService#loadMonthlyCourseware(java.lang.Long)
     */
    public MarketMonthlyCourseware loadMonthlyCourseware(Long monthlyId) {
        return genericDaoSupport.load(MarketMonthlyCourseware.class, monthlyId);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.market.service.CoursewareService#listBatchCourseware(co.fitstart.entity.market.MarketMonthlyCourseware)
     */
    public List<MarketBatchCourseware> listBatchCourseware(MarketMonthlyCourseware monthly) {
        return coursewareDao.listBatchCourseware(monthly);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.market.service.CoursewareService#loadBatchCourseware(java.lang.Long)
     */
    public MarketBatchCourseware loadBatchCourseware(Long batchId) {
        return genericDaoSupport.load(MarketBatchCourseware.class, batchId);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.market.service.CoursewareService#loadBathchCourseware(co.fitstart.entity.market.MarketMonthlyCourseware)
     */
    public MarketBatchCourseware loadBatchCourseware(MarketMonthlyCourseware monthly) {
        return coursewareDao.loadUnfinishBatchCourseware(monthly, CoursewareStatus.FINISHED);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.market.service.CoursewareService#listDailyCourseware(co.fitstart.entity.market.MarketBatchCourseware, co.fitstart.entity.market.DailyStatus)
     */
    public List<MarketDailyCourseware> listDailyCourseware(MarketBatchCourseware batch, DailyStatus status) {
        return coursewareDao.listFinishedDailyCourseware(batch, status);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.market.service.CoursewareService#loadDailyCourseware(java.lang.Long)
     */
    public MarketDailyCourseware loadDailyCourseware(Long dailyId) {
        return genericDaoSupport.load(MarketDailyCourseware.class, dailyId);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.market.service.CoursewareService#loadDailyCourseware(co.fitstart.entity.market.MarketBatchCourseware)
     */
    public MarketDailyCourseware loadDailyCourseware(MarketBatchCourseware batch) {
        return coursewareDao.loadDailyCourseware(batch, DailyStatus.FINISHED);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.market.service.CoursewareService#startTrain(co.fitstart.entity.market.MarketDailyCourseware, co.fitstart.entity.user.User)
     */
    public void startTrain(MarketDailyCourseware dailyCourseware, User user) {
        // 1. save training record
        TrainingRecord trainingRecord = coursewareDao.getTrainingRecord(dailyCourseware.getId(), user.getId());
        if(trainingRecord == null) {
            TrainingRecord record = new TrainingRecord(dailyCourseware, user, TrainingStatus.ACTING);
            genericDaoSupport.save(record);
        } else {
            genericDaoSupport.update(trainingRecord.changeStatus(TrainingStatus.ACTING).editTrainingTime(new Date()));
        }
        
        // 2. update dailyCourseware's status
        genericDaoSupport.update(dailyCourseware.startAction());
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.market.service.CoursewareService#finishTrain(co.fitstart.entity.market.MarketDailyCourseware, co.fitstart.entity.user.User)
     */
    public void finishTrain(MarketDailyCourseware dailyCourseware, User user) {
        // Step1. change training record's status to finished
        TrainingRecord record = coursewareDao.getTrainingRecord(dailyCourseware.getId(), user.getId());
        if(record == null) {
            record = new TrainingRecord(dailyCourseware, user, TrainingStatus.FINISHED);
            genericDaoSupport.save(record);
        } else {
            genericDaoSupport.update(record.changeStatus(TrainingStatus.FINISHED).editTrainingTime(new Date()));
        }
        
        // Step2. change dailyCourseware's status to finished
        int signinOrder = this.countDailyTrainingRecord(new Date(), user);
        genericDaoSupport.update(dailyCourseware.finishAction(signinOrder + 1022));
        
        // Step3. add credit to user
        user = this.addSigninCredit(dailyCourseware, user);
        
        // Step4. if this user can get achievement
        this.getAchievement(dailyCourseware.getBatch().getMonthly().getCourseRound(), user);
        
        // Step5. if this batch's all daily is finished then finish this batch and change the next batch's status
        this.finishBatchCourseware(dailyCourseware);
        
        // Step6. if this monthly's all batch is finished then finish this monthly and change the next monthly's status
        this.finishMonthlyCourseware(dailyCourseware);
    }
    
    /**
     * 
     * @param dailyCourseware
     * @param user
     */
    private User addSigninCredit(MarketDailyCourseware dailyCourseware, User user) {
        
        // 1. add signin credit
        user = this.creditRecord(50, "签到积分", user);
        
        // 2. whether punch the clock for 7 days
        // 2.1 whether punch the clock yesterday
        Date yesterday = DateUtils.addDays(new Date(), -1);
        TrainingRecord yesterdayRecord = coursewareDao.getTrainingRecord(user.getId(),
                                                                         com.demo2do.core.utils.DateUtils.parseDate(DateFormatUtils.format(yesterday, "yyyy-MM-dd") + " 00:00:00", "yyyy-MM-dd HH:mm:ss"),
                                                                         com.demo2do.core.utils.DateUtils.parseDate(DateFormatUtils.format(yesterday, "yyyy-MM-dd") + " 23:59:59", "yyyy-MM-dd HH:mm:ss"));
        // 2.2 if punch the clock yesterday then update round's insistDays
        MarketCourseRound courseRound = dailyCourseware.getBatch().getMonthly().getCourseRound();
        if(yesterdayRecord != null) {
            // 2.3 if round's insistDays equals 6 days 
            if(courseRound.getInsistDays() == 6) {
                user = this.creditRecord(350, "连续7天打卡奖励", user);
                courseRound.resetInsistDays();
            } else {
                courseRound.editInsistDays();
            }
            genericDaoSupport.update(user);
        } else {
            courseRound.editInsistDays();
        }
        
        // update courseRound's signin days and minutes
        genericDaoSupport.update(courseRound.editSigninDays().editSigninMinutes(dailyCourseware.getMinutes()));
        
        // 3. if this user is inviter
        MarketRecommendRecord recommendRecord = coursewareDao.getRecommendRecord(user.getOpenid());
        if(recommendRecord != null && !recommendRecord.isSignined()) {
            // 3.1 update recommendRecord first
            genericDaoSupport.update(recommendRecord.signin());
            
            // 3.2 add credit
            User recommender = userDao.loadUser(recommendRecord.getRecommender());
            this.creditRecord(1000, "推荐用户奖励", recommender);
        }
        
        return user;
    }
    
    /**
     * 
     * @param courseRound
     * @param user
     */
    private void getAchievement(MarketCourseRound courseRound, User user) {
        int signinDays = courseRound.getSigninDays();
        if (amounts.contains(signinDays)) {
            // 新建成就
            MarketAchievement achievement = new MarketAchievement(AchievementType.amountOf(signinDays), AchievementStatus.RECEIVING, user);
            genericDaoSupport.save(achievement);
            
            // 发送短信提醒
            String url = path + "/profile/achievements";
            smsSender.sendAchievementNotice(user.getMobile(), signinDays, url);
        }
    }
    
    /**
     * 
     * @param credit
     * @param description
     * @param user
     * @return
     */
    private User creditRecord(int credit, String description, User user) {
        user.editCredit(credit);
        CreditRecord creditRecord = new CreditRecord(credit, description, user);
        genericDaoSupport.save(creditRecord);
        return user;
    }
    
    /**
     * 
     * @param dailyCourseware
     */
    private void finishBatchCourseware(MarketDailyCourseware dailyCourseware) {
        MarketBatchCourseware batchCourseware = dailyCourseware.getBatch();
        List<MarketDailyCourseware> dailyCoursewares = coursewareDao.listDailyCourseware(batchCourseware, DailyStatus.FINISHED);
        if(dailyCoursewares.isEmpty()) {
            genericDaoSupport.update(batchCourseware.changeStatus(CoursewareStatus.FINISHED));
            
            MarketBatchCourseware nextBatch = coursewareDao.loadBatchCourseware(batchCourseware.getMonthly(), batchCourseware.getTemplate().getSorting()+1);
            if(nextBatch != null) {
                genericDaoSupport.update(nextBatch.changeStatus(CoursewareStatus.SIGNINING));
            }
        }
    }
    
    /**
     * 
     * @param dailyCourseware
     */
    private void finishMonthlyCourseware(MarketDailyCourseware dailyCourseware) {
        MarketMonthlyCourseware monthlyCourseware = dailyCourseware.getBatch().getMonthly();
        List<MarketBatchCourseware> batchCoursewares = coursewareDao.listBatchCourseware(monthlyCourseware, CoursewareStatus.FINISHED);
        if(batchCoursewares.isEmpty()) {
            genericDaoSupport.update(monthlyCourseware.changeStatus(CoursewareStatus.FINISHED));
            
            MarketMonthlyCourseware nextMonthly = coursewareDao.loadMonthlyCourseware(monthlyCourseware.getCourseRound(), monthlyCourseware.getTemplate().getSorting()+1);
            if(nextMonthly != null) {
                genericDaoSupport.update(nextMonthly.changeStatus(CoursewareStatus.SIGNINING));
            }
        }
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.market.service.CoursewareService#getFinishedAction(co.fitstart.entity.market.MarketCourseRound)
     */
    public int getFinishedAction(MarketCourseRound courseRound) {
        int finishedAction = 0;
        
        List<MarketMonthlyCourseware> monthlyCoursewares = coursewareDao.listMonthlyCourseware(courseRound);
        
        List<MarketBatchCourseware> batches = coursewareDao.listBatchCourseware(monthlyCoursewares);
        
        List<MarketDailyCourseware> dailyCoursewares = coursewareDao.listDailyCourseware(batches, DailyStatus.FINISHED);
        for(MarketDailyCourseware dailyCourseware : dailyCoursewares) {
            finishedAction = finishedAction + dailyCourseware.getTemplate().getActionAmount();
        }
        
        return finishedAction;
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.market.service.CoursewareService#countDailyTrainingRecord(java.util.Date, co.fitstart.entity.user.User)
     */
    public int countDailyTrainingRecord(Date date, User user) {
        Date startTime = com.demo2do.core.utils.DateUtils.parseDate(DateFormatUtils.format(date, "yyyy-MM-dd") + " 00:00:00", "yyyy-MM-dd HH:mm:ss");
        Date endTime = com.demo2do.core.utils.DateUtils.parseDate(DateFormatUtils.format(date, "yyyy-MM-dd") + " 23:59:59", "yyyy-MM-dd HH:mm:ss");
        
        return coursewareDao.countTrainingRecord(TrainingStatus.FINISHED, startTime, endTime, user.getId());
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.market.service.CoursewareService#getTrainRecord(co.fitstart.entity.user.User, java.util.Date)
     */
    public TrainingRecord getTrainRecord(User user, Date date) {
        Date startTime = com.demo2do.core.utils.DateUtils.parseDate(DateFormatUtils.format(date, "yyyy-MM-dd") + " 00:00:00", "yyyy-MM-dd HH:mm:ss");
        Date endTime = com.demo2do.core.utils.DateUtils.parseDate(DateFormatUtils.format(date, "yyyy-MM-dd") + " 23:59:59", "yyyy-MM-dd HH:mm:ss");
        
        return coursewareDao.getTrainingRecord(user.getId(), TrainingStatus.FINISHED, startTime, endTime);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.market.service.CoursewareService#particate(java.lang.String, co.fitstart.entity.market.MarketCourse, java.lang.String)
     */
//    public void particate(String userOpenid, MarketCourse course, String recommender) {
//        // Step1. if recommender is not null then create market recommend record first
//        if(!StringUtils.isEmpty(recommender)) {
//            MarketRecommendRecord recommendRecord = new MarketRecommendRecord(recommender, userOpenid);
//            genericDaoSupport.save(recommendRecord);
//        }
//        
//        // Step2. create marketCourseRound
//        MarketCourseRound courseRound = new MarketCourseRound(course, user);
//        genericDaoSupport.save(courseRound);
//        
//        // Step3. create marketMonthlyCourseware, marketBatchCourseware, marketDailyCourseware and marketCourseware
//        List<MarketMonthlyCoursewareTemplate> monthlyTemplates = coursewareDao.listMonthlyCoursewareTemplate(course);
//        for(MarketMonthlyCoursewareTemplate monthlyTemplate : monthlyTemplates) {
//            MarketMonthlyCourseware monthly = new MarketMonthlyCourseware(monthlyTemplate, courseRound);
//            genericDaoSupport.save(monthly);
//            
//            List<MarketBatchCoursewareTemplate> batchTemplates = monthlyTemplate.getBatchCoursewares();
//            for(MarketBatchCoursewareTemplate batchTemplate : batchTemplates) {
//                MarketBatchCourseware batch = new MarketBatchCourseware(batchTemplate, monthly);
//                genericDaoSupport.save(batch);
//                
//                List<MarketDailyCoursewareTemplate> dailyTemplates = batchTemplate.getDailycoursewares();
//                for(MarketDailyCoursewareTemplate dailyTemplate : dailyTemplates) {
//                    MarketDailyCourseware daily = new MarketDailyCourseware(dailyTemplate, batch);
//                    genericDaoSupport.save(daily);
//                }
//            }
//        }
//    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.market.service.CoursewareService#getMarketWechatImage(co.fitstart.entity.market.MarketCourse)
     */
    public MarketWechatImage getMarketWechatImage(MarketCourse course) {
        return coursewareDao.loadMarketWechatImage(course);
    }

}
